home *** CD-ROM | disk | FTP | other *** search
/ NetNews Offline 2 / NetNews Offline Volume 2.iso / news / comp / std / c++ / 1028 < prev    next >
Encoding:
Internet Message Format  |  1996-08-06  |  12.6 KB

  1. Path: fido.asd.sgi.com!austern
  2. From: kanze@gabi-soft.fr (J. Kanze)
  3. Newsgroups: comp.std.c++
  4. Subject: Re: sample auto_ptr template
  5. Date: 10 Apr 1996 09:56:31 PDT
  6. Organization: GABI Software, Sarl.
  7. Approved: austern@isolde.mti.sgi.com
  8. Message-ID: <KANZE.96Apr10111407@gabi.gabi-soft.fr>
  9. References: <009A04DA6A831C40.49800EAC@ittpub.nl>
  10.     <bill-0504961003150001@bgibbons.vip.best.com> <4k4noe$igl@jabba.lehman.com>
  11.     <bill-0804960932250001@bgibbons.vip.best.com> <4kcr2d$p03@jabba.lehman.com>
  12. NNTP-Posting-Host: isolde.mti.sgi.com
  13. X-Original-Date: 10 Apr 1996 09:14:07 GMT
  14. In-Reply-To: ajay@lehman.com's message of 09 Apr 1996 09:58:11 PDT
  15. X-Auth: PGPMoose V1.1 PGP comp.std.c++
  16.     iQBVAwUBMWvoQUy4NqrwXLNJAQENFgH+LlqUvSg907kaiS3v8Pma5kwpx5m1OjFd
  17.     9Evfclq1BXxJ65gXKfg9OPuJfxxIFX3iAB1Eh0NYEl2eL1tZQv8E8w==
  18.     =DiuH
  19. Originator: austern@isolde.mti.sgi.com
  20.  
  21. In article <4kcr2d$p03@jabba.lehman.com> ajay@lehman.com (Ajay Kamdar)
  22. writes:
  23.  
  24. |> In article <bill-0804960932250001@bgibbons.vip.best.com>,
  25. |> Bill Gibbons <bill@gibbons.org> wrote:
  26. |> >In article <4k4noe$igl@jabba.lehman.com>, ajay@lehman.com (Ajay Kamdar) wrote:
  27. |> >
  28. |>            [ snip ]
  29. |> >> 
  30. |> >> What's wrong with this? It doesn't require copy semantics
  31. |> >> for auto_ptr. Yet both the caller and the callee
  32. |> >> are exception safe and there is no loss of clarity.
  33. |> >
  34. |> >The problem is that it requires handling the raw pointer.
  35. |> >Any time you transfer ownership by using release() to extract
  36. |> >the raw pointer and then later use the raw pointer to
  37. |> >construct another auto_ptr, there is a window where there is
  38. |> >no exception safety.
  39. |> >
  40.  
  41. |> You didn't say whether the example I gave was exception
  42. |> safe or not.
  43.  
  44. But I did.  It's not.
  45.  
  46. |> To reiterate the main points of the example
  47. |>    + The caller directly initializes an auto_ptr with
  48. |>      the returned value from the calee.
  49. |>      auto_ptr<X> ptr(get_X());
  50.  
  51. |>    + The callee releases the pointer from it's own
  52. |>      auto_ptr in it's return statement.
  53.  
  54. |> No copy constructors are involved in the return. Hence
  55. |> there is no possibility of an exception thrown from
  56. |> a user defined copy constructor. So which window are
  57. |> you referring to during which there is no exception
  58. |> safety? I claim there is none.
  59.  
  60. The destructors of any local variables in get_X.
  61.  
  62. |> >Of course you can carefully craft the code to make sure that
  63. |> >no exceptions can be propagated during the window.  But there
  64. |> >are two problems:
  65.  
  66. |> The example I gave is straight forward C++ programming; no
  67. |> contortions or careful coding are required. The coding
  68. |> pattern is simple enough that even junior programmers can
  69. |> be taught to follow it easily.
  70.  
  71. The only real solution I can think of to be 100% exception safe would be
  72. to write get_X as follows:
  73.  
  74.     T*
  75.     get_X()
  76.     {
  77.         auto_ptr< T >    localTmp ;
  78.         {
  79.             //  All of the work here, including assigning the actual
  80.             //  pointer to localTmp, and above all, all local variables
  81.             //  except localTmp here.
  82.         }
  83.         return localTmp.release() ;
  84.     }
  85.  
  86. While the pattern isn't that complicated, it is certainly not what we
  87. are used to, and is likely to be forgotten from time to time.
  88.  
  89. |> Many other more useful extensions have been voted down by
  90. |> the commitee on the grounds that the requested extension
  91. |> could be implemented by existing mechanisms within the
  92. |> language, even if those techniques required a fair
  93. |> amount of work for the programmer. That stance cannot be
  94. |> more applicable than in the case of the copy semantics
  95. |> of auto_ptr -- there is simply no need for such semantics
  96. |> because identical exception safety can be achieved by
  97. |> a simple coding pattern.
  98.  
  99. I imagine that the committee is more tolerant with regards to extensions
  100. which are purely library issues.  Library issues are almost guaranteed
  101. by their very nature not to have a surprise side effect in other parts
  102. of the language.
  103.  
  104. |> >  (1) The maintainers of the code may not be as careful about
  105. |> >      exception safety.  When everything is handled by
  106. |> >      auto_ptr the risk of bugs creeping in is smaller.  Such
  107. |> >      bugs are very difficult to find by testing.
  108.  
  109. |> Let's analyze which code is more difficult to maintain
  110. |> and difficult to debug -- one using the copy semantics
  111. |> of auto_ptr or one wihout copy semantics of auto_ptr.
  112.  
  113. |>   With copy semantics
  114. |>   ===================
  115. |>      If the copy ctor of auto_ptr taking a const auto_ptr&
  116. |>      releases its raw pointer by casting away const,
  117. |>      a programmer can accidentaly "lose" the raw pointer from
  118. |>      within an auto_ptr -- even if the programmer was working
  119. |>      with a const auto_ptr. This occurrs because auto_ptr
  120. |>      violates const correctness and releases the raw pointer
  121. |>      from the raw object.
  122.  
  123. |>      If the copy ctor of the auto_ptr taking a const auto_ptr&
  124. |>      takes ownership of the raw pointer by casting away const
  125. |>      and setting a flag (the latest proposal), the programmer
  126. |>      can end up with a dangling pointer in the original
  127. |>      auto_ptr if the new auto_ptr destructs first and deletes
  128. |>      the raw pointer.
  129.  
  130. |>      It is possible that even extensive testing may not
  131. |>      uncover such a problem on a less frequently traversed
  132. |>      path in the code. Even if the path is traversed during
  133. |>      testing, an error may or may not be generated when using
  134. |>      the dangling pointer. Hence it is very possible that the
  135. |>      error might escape it to production code.
  136.  
  137. I think the question here is one of patterns.  The auto_ptr is designed
  138. to implement simply certain basic, frequently occuring patterns.  If it
  139. is used for other things, it is unsafe.
  140.  
  141. Because its only legitimate use *is* these frequently occuring patterns,
  142. any other use is easily detected by code review.  The alternative, using
  143. raw pointers, is more dangerous, because raw pointers have so many uses;
  144. the error is not immediately apparent.
  145.  
  146. |>   Without copy semantics of auto_ptr
  147. |>   ==================================
  148. |>     By eliminating any surprises due to the standard
  149. |>     auto_ptr violating const correctness and leaving
  150. |>     behind dangling pointers, program correctness is
  151. |>     actually *improved*.
  152.  
  153. |>     But for the moment assume that even the simple pattern
  154. |>     of using auto_ptr (release() on return in callee,
  155. |>     direct initialization of auto_ptr in caller) is
  156. |>     not followed correctly in some situation. Two major
  157. |>     scenarios are possible:
  158. |>        1. The error happens on a frequently travesed path
  159. |>       in the code, resulting in frequent ommission to
  160. |>       call delete.
  161.  
  162. |>       In such situations, any of the many good leak
  163. |>       detectors on the market will show the error
  164. |>       during testing. The error will be fixed
  165. |>       before it reached production.
  166.  
  167. |>       2. The error is rare enough that it occurs only in
  168. |>      production.
  169.  
  170. |>      By definition, the error is rare. So the application
  171. |>      will lose some resource each time. In practice,
  172. |>      each occurence of a failure to delete a resource is
  173. |>      not disastorous on its own, and the application will
  174. |>      go on.
  175.  
  176.  
  177. |> So which one is better for the C++ programmer? An approach
  178. |> which can lead to disastorous surprises at run-time
  179. |> (auto_ptr with copy semantics approach) definitely does
  180. |> not look the right choice under any stretch of imagination.
  181.  
  182. In short, you are suggesting that memory leaks are OK, as long as they
  183. don't occur too often.  This isn't the case for my applications.
  184.  
  185. |> >  (2) The interface of get_X does not implicitly document
  186. |> >      that the returned pointer refers to an object which
  187. |> >      should be automatically deleted on an exception.
  188. |>                                ^^^^^^^^^^^^^^^^^^^^^^^
  189.  
  190. |> Now we are getting close to what I firmly believe is the
  191. |> real driving reason behind the push for auto_ptr to
  192. |> have copy semantics.
  193.  
  194. |> Note that in the above quoted text, the reference to
  195. |> exceptions in "deleted on an exception" is irrelevant.
  196. |> In this case, the caller must always delete the ptr
  197. |> allocated by the callee() -- exception or no exception.
  198. |> Hence the use of auto_ptr in this case has nothing to
  199. |> do with exception safety. But it has everything to do
  200. |> with trying to replace documentation accompanying
  201. |> get_X() saying that the caller must delete the pointer.
  202.  
  203. No.  The caller cannot possibly delete the pointer, since the pointer is
  204. never assigned at the caller side if an exception occurs.  The caller
  205. never even sees the pointer.
  206.  
  207. The whole point of the copy semantics is that there is a period of time
  208. after the evaluation of the return value in the return statement when
  209. neither the callee nor the caller have access to the pointer.  Using a
  210. temporary class object means, however, that this object does have
  211. access, and can delete the object if an exception is thrown.  The
  212. alternative is somehow to ensure that no exception can be thrown.  This
  213. is not always trivial.
  214.  
  215. |> In isolation, it is a good goal to replace comments
  216. |> which prescribe certain behavior with code constructs
  217. |> which automatically take care of the issue. By
  218. |> giving copy semantics to auto_ptr, the need to
  219. |> document transfer of ownership of pointer is sourght
  220. |> to be eliminated. I firmly believe that *this* is the
  221. |> real reason behind the copy semantics of auto_ptr, with
  222. |> exception safety only a peripheral issue at best. 
  223. |> Unfortunately, it is abundantly clear that this attempt
  224. |> at replacing documentation by giving auto_ptr copy
  225. |> semantics has pretty high costs.
  226.  
  227. There are two separate problems: the unexpected semantics of assignment,
  228. and the exception safety.  Because of the way the language is defined,
  229. we can only define auto_ptr to handle one correctly; the other must be
  230. left to comments.  (I think that we more or less agree up until this
  231. point.)  The question is: which one?
  232.  
  233. IMHO, requiring the programmer to handle the exception safety is
  234. non-intuitive, difficult, and error prone.  While I agree that requiring
  235. the programmer to handle the strange copy semantics is non-intuitive, it
  236. is relatively easy to check.  In all *valid* uses of auto_ptr, an
  237. auto_ptr which is copied should immediately go out of scope.  This is
  238. trivially true in the case of source functions, like get_X.  The only
  239. point which requires a little bit of attention is when copying to a
  240. sink; I would generally try and arrange my code so that the call to the
  241. sinking function is the last thing in the block in which the auto_ptr
  242. was declared, but I'm not certain that this is always possible or
  243. natural.  (In practice, until now, it has always been the most natural
  244. way of writing the code anyway.)
  245.  
  246. Any attempts to assign between two declared auto_ptr's, or to initialize
  247. an auto_ptr with another declared auto_ptr, should be caught by code
  248. review.
  249.  
  250. |> I strongly urge those who can do something about this
  251. |> (those on the committee) to step back and separate
  252. |> the wheat from the chaff. Don't make auto_ptr harder
  253. |> to use for *all* C++ programmers by allowing it to
  254. |> have copy semantics. Make a separate class
  255. |> (taligent_ptr) which has the copy semantics for
  256. |> those who *choose* to use it, rather than forcing
  257. |> everyone to program with an unfortunate choice.
  258.  
  259. I believe that John Skaller had a proposal with something like six
  260. different pointer types.  The committee found this a little too
  261. complicated for its likings (I would agree), and settled on defining two
  262. (or maybe three).
  263.  
  264. For various reasons, it was unable to find a definition of counted_ptr
  265. which would obtain a concensus.  In some ways, this is regrettable, but
  266. if you look at the implementations of this class in Barton and Nackman,
  267. on one hand, and Meyers II on the other, you can see that in fact, there
  268. are contradictory goals to be met.  I have no problem understanding why
  269. no concensus could be reached.
  270.  
  271. In the case of auto_ptr, those (like Bill Gibbons) with extensive
  272. practical experience actually using such a pointer indicated that to be
  273. useful, it needed copy semantics.  (I might add that this corresponds
  274. exactly to my experience.)  Without copy semantics, it simply wouldn't
  275. be used in most cases.  (Or programmers would resort to tricks such as
  276. returning the pointer as a raw pointer, with the resulting loss of
  277. exception safety.)
  278. -- 
  279. James Kanze           (+33) 88 14 49 00          email: kanze@gabi-soft.fr
  280. GABI Software, Sarl., 8 rue des Francs Bourgeois, 67000 Strasbourg, France
  281. Conseils en informatique industrielle --
  282.                             -- Beratung in industrieller Datenverarbeitung
  283. ---
  284. [ comp.std.c++ is moderated.  To submit articles: Try just posting with your 
  285.                 newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  286.   comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  287.   Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  288.   Comments? mailto:std-c++-request@ncar.ucar.edu 
  289. ]
  290.